
package com.ifcar.app.view;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.graphics.Rect;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.LinearLayout;

import java.util.ArrayList;

public class FloorsView extends LinearLayout {

    private boolean mDataChanged;

    private Object mFloorsData;

    private FloorBinder mFloorBinder;

    private int mPadding;

    private FloorViewHolder mViewHolder;

    private float mStrokeWidth;

    private RectF mRectF = new RectF();

    private Paint mRectStrokePaint = new Paint();

    private Paint mRectColorPaint = new Paint();

    private Rect mClipRect = new Rect();

    public FloorsView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }

    public FloorsView(Context context) {
        super(context);
        init(context);
    }

    private void init(Context context) {
        setOrientation(VERTICAL);
        final float density = context.getResources().getDisplayMetrics().density;
        mPadding = (int) (density * 2);
        mStrokeWidth = 0.5f * density;
        mRectStrokePaint.setStrokeWidth(mStrokeWidth);
        mRectStrokePaint.setStyle(Style.STROKE);
    }

    public void setFloorColor(int color) {
        mRectColorPaint.setColor(color);
        invalidate();
    }

    public void setFloorStrokeColor(int color) {
        mRectStrokePaint.setColor(color);
        invalidate();
    }

    @Override
    protected void dispatchDraw(Canvas canvas) {
        final int count = getChildCount();
        if (count > 0) {
            if (!canvas.getClipBounds(mClipRect)) {
                mClipRect.setEmpty();
            }
            View child = getChildAt(count - 1);
            mRectF.set(child.getLeft(), child.getLeft(), child.getRight(), child.getBottom());

            canvas.drawRect(mRectF, mRectColorPaint);
        }
        
        super.dispatchDraw(canvas);
        
        if (count > 0) {
            for (int i = count - 1; i >= 0; i--) {
                View child = getChildAt(i);
                mRectF.set(child.getLeft() + mStrokeWidth, child.getLeft() + mStrokeWidth, child.getRight() - mStrokeWidth,
                        child.getBottom() - mStrokeWidth);
                canvas.drawRect(mRectF, mRectStrokePaint);
            }
        }
    }

    @Override
    protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
        if (mClipRect.top >= child.getBottom() || mClipRect.bottom <= child.getTop()) {
            return false;
        }
        return super.drawChild(canvas, child, drawingTime);
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        if (mDataChanged) {
            mDataChanged = false;
        }

        super.onLayout(changed, left, top, right, bottom);
    }

    public void setFloorBinder(FloorBinder binder) {
        mFloorBinder = binder;
    }

    public void setFloorViewHolder(FloorViewHolder holder) {
        mViewHolder = holder;
    }

    public FloorViewHolder getFloorViewHolder() {
        return mViewHolder;
    }

    public int getCount() {
        if (mFloorBinder == null) {
            return 0;
        }
        return mFloorBinder.getFloorCount(mFloorsData);
    }

    public void setFloorsValue(Object datas) {
        mFloorsData = datas;
        mDataChanged = true;
        // requestLayout();
        if (mViewHolder == null) {
            mViewHolder = new FloorViewHolder();
        }
        mViewHolder.recycleFloorView(this);
        final int count = getCount();

        if (mFloorBinder != null && count > 0) {
            for (int i = 0; i < count; i++) {
                FloorView floor = getFloorView();
                LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) floor.getLayoutParams();
                if (lp == null) {
                    lp = new LinearLayout.LayoutParams(FrameLayout.LayoutParams.FILL_PARENT,
                            FrameLayout.LayoutParams.WRAP_CONTENT);
                    lp.gravity = Gravity.TOP | Gravity.CENTER;
                    addViewInLayout(floor, i, lp);
                } else {
                    attachViewToParent(floor, i, lp);
                }
                final int paddingCount = Math.min(count - i - 1, 4);
                lp.rightMargin = lp.leftMargin = paddingCount * mPadding;
                if (i == 0) {
                    lp.topMargin = Math.min(count, 4) * mPadding;
                } else {
                    lp.topMargin = 0;
                }

                mFloorBinder.bindFloor(floor, i, mFloorsData, count);
            }
        }

    }

    private FloorView getFloorView() {
        FloorView floorView = mViewHolder.getFloorView(this);

        if (floorView == null) {
            floorView = new FloorView(getContext(), this);

            View content = mFloorBinder.createFloorContentView(mFloorsData, floorView);
            FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) content.getLayoutParams();
            if (lp == null) {
                lp = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.FILL_PARENT,
                        FrameLayout.LayoutParams.WRAP_CONTENT);
            }
            floorView.addView(content, lp);
        }

        return floorView;
    }

    private static class FloorView extends FrameLayout {
        public FloorView(Context context, FloorsView floors) {
            super(context);
        }
    }

    public static class FloorViewHolder {
        private ArrayList<FloorView> mFloorCache = new ArrayList<FloorView>();

        // 这里都不加同步控制,因为都必须运行在主线程里

        public void recycleFloorView(FloorsView floors) {
            for (int i = floors.getChildCount() - 1; i >= 0; i--) {
                FloorView floorView = (FloorView) floors.getChildAt(i);
                mFloorCache.add(floorView);
            }
            floors.detachAllViewsFromParent();
        }

        public FloorView getFloorView(FloorsView floors) {
            return mFloorCache.size() > 0 ? mFloorCache.remove(0) : null;
        }
    }

    public interface FloorBinder {
        public View createFloorContentView(Object data, ViewGroup parent);

        public void bindFloor(ViewGroup floor, int position, Object data, int floorCount);

        public int getFloorCount(Object data);
    }

}
